<?php

namespace yunj\app\admin\service\member;

use think\facade\Db;
use yunj\core\enum\RedisKey;
use yunj\app\admin\enum\State;
use yunj\app\admin\service\Service;
use yunj\app\admin\model\AdminMember;
use yunj\core\enum\RedisKeyPrefixEnum;
use think\model\relation\BelongsToMany;

class MemberLoginService extends Service {

    /**
     * token有效期，单位秒/s。默认10天
     * @var int
     */
    protected $tokenExpireTime = 60 * 60 * 24 * 10;

    /**
     * 授权登录
     * @param AdminMember $member
     * @param string|null $ip
     * @return array    响应token对应有效时间
     */
    final public function login(AdminMember $member, ?string $ip = null): array {
        $token = admin_member_login_controller()->generateToken($member);
        $this->saveToken($token, $member->id);
        $ip = $ip ?: request_ip();
        $time = time();
        $member->save([
            'last_login_ip' => $ip,
            'last_login_time' => $time
        ]);

        // 日志补充记录
        admin_member_log(['member_id' => $member->id, 'data_id' => $member->id, 'desc' => ($member->name) . ' 登录成功']);

        // 触发事件
        event('AdminMemberLogin', $member);
        // 记录登录日志
        return [
            'token' => $token,
            'expire' => $this->tokenExpireTime,
        ];
    }

    /**
     * 退出登录
     * @param string $token
     */
    final public function logout(string $token = ''): void {
        $token = $token ?: admin_member_login_controller()->getRequestToken();
        if (!$token) return;
        $this->clearToken($token);
        // 日志补充记录
        if ($member = $this->getMember()) {
            admin_member_log(['data_id' => $member->id, 'desc' => ($member->name) . ' 退出登录成功']);
        }
        // 触发事件
        event('AdminMemberLogout', $this->getMember());
    }

    /**
     * 验证登录有效性
     * @param string $token
     * @return AdminMember|false
     */
    final public function validate(string $token = '') {
        $token = $token ?: admin_member_login_controller()->getRequestToken();
        if (!$token) return false;

        // 避免重复校验，去反复查询
        if (request()->$token) {
            return request()->$token;
        }

        $member = self::getMemberByToken($token);
        if (!$member) return false;
        $_token = admin_member_login_controller()->generateToken($member);
        if ($token !== $_token) return false;
        $this->saveToken($token, $member->id);

        request()->$token = $member;
        return $member;
    }

    // 根据 token 获取 member 数据
    private static function getMemberByToken(string $token) {
        if (!$token) return null;

        $memberId = self::getMemberIdByTokenObj($token)->getCacheValue();
        if (!$memberId) return null;

        $member = self::getAdminMemberModel()->with(['roles' => function ($belongsToMany) {
            /** @var BelongsToMany $belongsToMany */
            $belongsToMany->getQuery()->where('state', State::NORMAL);
        }])->where([
            ['id', '=', $memberId],
            ['state', '=', State::NORMAL],
        ])->find();
        if (!$member) return null;
        if (!$member->roles) return null;
        return $member;
    }

    /**
     * @param string $token
     * @return RedisKey
     */
    private static function getMemberIdByTokenObj(string $token) {
        /** @var RedisKey $obj */
        $obj = RedisKey::ADMIN_MEMBER_ID_BY_TOKEN();
        $obj->setArgs($token);
        return $obj;
    }

    // 保存token
    private function saveToken(string $token, int $memberId): bool {
        $res = self::getMemberIdByTokenObj($token)->setCacheValue($memberId, $this->tokenExpireTime);
        return !!$res;
    }

    // 清除token
    private function clearToken(string $token): void {
        self::getMemberIdByTokenObj($token)->delCacheValue();
    }

}